﻿using System;
using System.Collections.Generic;
using System.Linq;

namespace RbmlVisualizer.Logging
{
    /// <summary>
    /// A logging service
    /// </summary>
    public class LoggingService : ILoggingService
    {
        private readonly IList<LogMessage> _loggingMessages;
        public event EventHandler<MessageLoggedEventArgs> MessageLogged;
        public LogLevelType LogLevel { get; set; }

        //For now this is a singleton
        public static LoggingService Instance
        {
            get { return _instance ?? (_instance = new LoggingService()); }
        }

        private static LoggingService _instance;

        private LoggingService()
        {
            LogLevel = LogLevelType.All;
            _loggingMessages = new List<LogMessage>();
        }

        /// <summary>
        /// Returns a string copy of the logging messages
        /// </summary>
        public IList<string> LoggingMessages
        {
            get
            {
                return _loggingMessages.Select(loggingMessage => loggingMessage.ToString()).ToList();
            }
        }

        /// <summary>
        /// Gets all the log messages at or below levelType
        /// </summary>
        /// <param name="levelType">The level to get the log messages for.</param>
        /// <returns>A list of the LogMessage objects.</returns>
        public IList<LogMessage> GetLoggingMessagesForLevel(LogLevelType levelType)
        {
            var list = new List<LogMessage>();
            foreach (var loggingMessage in _loggingMessages)
            {
                if ((loggingMessage.LevelType & levelType) > 0 && loggingMessage.LevelType <= levelType)
                    list.Add(loggingMessage);
            }
            return list;
            //return _loggingMessages.Where(m => (m.LevelType & levelType) > 0).ToList();
        }

        /// <summary>
        /// Clears all the logmessages
        /// </summary>
        public void Clear()
        {
            _loggingMessages.Clear();
        }

        /// <summary>
        /// Checks to see if the logger is currently logging level.
        /// </summary>
        /// <param name="level">The level to check.</param>
        /// <returns>true if the logger is monitoring level, false otherwise.</returns>
        public bool IsLoggingEnabledFor(LogLevelType level)
        {
            return (level & LogLevel) == level;
        }


        public void WriteDebug(Func<string> createMessageDelegate)
        {
            if (IsLoggingEnabledFor(LogLevelType.Debug))
                WriteDebug(createMessageDelegate(), null);
        }

        public void WriteInfo(Func<string> createMessageDelegate)
        {
            if (IsLoggingEnabledFor(LogLevelType.Information))
                WriteInfo(createMessageDelegate(), null);
        }

        public void WriteError(Func<string> createMessageDelegate)
        {
            if (IsLoggingEnabledFor(LogLevelType.Error))
                WriteError(createMessageDelegate(), null);
        }

        public void WriteInfo(string format, params object[] args)
        {
            WriteInfo(args == null ? format : string.Format(format, args));
        }

        public void WriteInfo(string message)
        {
            WriteMessageToLog(LogLevelType.Information, message);
        }

        public void WriteDebug(string format, params object[] args)
        {
            WriteDebug(args == null ? format : string.Format(format, args));
        }

        public void WriteDebug(string message)
        {
            WriteMessageToLog(LogLevelType.Debug, message);
        }

        public void WriteError(string format, params object[] args)
        {
            WriteError(args == null ? format : string.Format(format, args));
        }

        public void WriteError(string message)
        {
            WriteMessageToLog(LogLevelType.Error, message);
        }

        public void WriteException(Exception exception, string message)
        {
            WriteError(message);

            var ex = exception;

            WriteError("Unwinding exception stack:");

            while (ex != null)
            {
                WriteError(ex.Message);
                WriteError(ex.StackTrace);

                ex = ex.InnerException;
            }
        }

        public void WriteException(Exception exception, string format, params object[] args)
        {
            WriteException(exception, args == null ? format : string.Format(format, args));
        }

        /// <summary>
        /// Core function to the logger, it writes the message to the logger at logLevel.
        /// </summary>
        /// <param name="logLevel">Level to write the log message at.</param>
        /// <param name="message">The message to write to the log.</param>
        private void WriteMessageToLog(LogLevelType logLevel, string message)
        {
            if (!IsLoggingEnabledFor(logLevel))
                return;

            var logMessage = new LogMessage(message, logLevel, DateTime.Now);
            _loggingMessages.Add(logMessage);
            OnMessageLogged(logMessage);

#if DEBUG
            System.Diagnostics.Debug.WriteLine(logMessage.ToString());
#endif
        }

        protected void OnMessageLogged(LogMessage logMessage)
        {
            if (MessageLogged != null)
                MessageLogged(this, new MessageLoggedEventArgs(logMessage));
        }
    }
}
